﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Samples.ServiceHosting.StorageClient;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Microsoft.Win32;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Configuration;
using System.Security;

namespace BlobExplorer
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class ExplorerWindow : Window
    {
        public BlobModel Model { get; private set; }

        private void UpdateStatus(object sender, TransferEventArgs e)
        {
            string statusText = "";
            if (e.DownloadNames.Count > 0)
            {
                statusText = string.Format("Downloading: {0}", e.DownloadNames.GetDelimitedList());

                if (e.UploadNames.Count > 0)
                {
                    statusText += " : ";
                }
            }

            if (e.UploadNames.Count > 0)
            {
                statusText += string.Format("Uploading: {0}", e.UploadNames.GetDelimitedList());
            }

            status.Text = statusText;

        }

        private string standardTitle = "Azure Blob Storage Explorer";

        public bool Connected
        {
            get { return (bool)GetValue(ConnectedProperty); }
            set { SetValue(ConnectedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Connected.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ConnectedProperty =
            DependencyProperty.Register("Connected", typeof(bool), typeof(ExplorerWindow));

        public ExplorerWindow()
        {
            InitializeComponent();

            Model = new BlobModel(SynchronizationContext.Current);
            Model.TransferListChanged += UpdateStatus;

            DataContext = this;

            CommandBindings.Add(new CommandBinding(Commands.Connect, OnConnect));
            CommandBindings.Add(new CommandBinding(Commands.Disconnect, OnDisconnect));
            CommandBindings.Add(new CommandBinding(Commands.AddContainer, OnAddContainer));
            CommandBindings.Add(new CommandBinding(Commands.DeleteContainer, OnDeleteContainer));
            CommandBindings.Add(new CommandBinding(Commands.AddBlob, OnAddBlob));
            CommandBindings.Add(new CommandBinding(Commands.DeleteBlob, OnDeleteBlob));
            CommandBindings.Add(new CommandBinding(Commands.GetBlob, OnGetBlob));
            CommandBindings.Add(new CommandBinding(Commands.OpenBlob, OnOpenBlob));
            CommandBindings.Add(new CommandBinding(Commands.Refresh, OnRefresh));
            CommandBindings.Add(new CommandBinding(Commands.About, OnAbout));
            CommandBindings.Add(new CommandBinding(Commands.CopyUri, OnCopyUri));
            CommandBindings.Add(new CommandBinding(Commands.ViewMetadata, OnViewMetadata));

            details.DragEnter += new DragEventHandler(details_DragEnter);
            details.MouseDoubleClick += new MouseButtonEventHandler(details_MouseDoubleClick);

            contents.SelectionChanged += new SelectionChangedEventHandler(ContainerSelected);
        }

        void details_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            OpenBlob();
        }

        void details_DragEnter(object sender, DragEventArgs e)
        {
            e.Effects = DragDropEffects.Copy;
        }

        void details_Drop(object sender, DragEventArgs e)
        {
            if (contents.SelectedItem == null)
            {
                MessageBox.Show("You must select a container to add blobs to");
                return;
            }
            if (e.Data.GetFormats().Contains("FileDrop"))
            {
                DataObject dataObject = e.Data as DataObject;
                StringCollection files = null;
                if (dataObject != null)
                {
                    files = dataObject.GetFileDropList();
                }
                else
                {
                    string[] fileArray = (string[])e.Data.GetData("FileNameW");
                    files = new StringCollection();
                    files.Add(fileArray[0]);
                }

                foreach (string file in files)
                {
                    Model.AddBlob((BlobContainer)contents.SelectedItem, System.IO.Path.GetFileName(file), file, null);
                }
            }
        }

        private void OnViewMetadata(object sender, ExecutedRoutedEventArgs e)
        {
            BlobProperties prop = (BlobProperties)details.SelectedItem;
            if (prop == null)
            {
                MessageBox.Show("You must select a blob to open");
                return;
            }

            BlobContainer container = (BlobContainer)contents.SelectedItem;

            prop = Model.GetBlobProperties(container, prop.Name);

            ViewMetadata vm = new ViewMetadata(prop.Metadata);
            vm.ShowDialog();
        }

        private void OnCopyUri(object sender, ExecutedRoutedEventArgs e)
        {
            Clipboard.SetText(((BlobProperties)details.SelectedItem).Uri.ToString());
        }

        void ContainerSelected(object sender, SelectionChangedEventArgs e)
        {
            PopulateBlobs();
        }

        private void OnOpenBlob(object sender, ExecutedRoutedEventArgs e)
        {
            OpenBlob();
        }

        private void OpenBlob()
        {
            BlobProperties prop = (BlobProperties)details.SelectedItem;
            if (prop == null)
            {
                MessageBox.Show("You must select a blob to open");
                return;
            }

            if (((BlobContainer)contents.SelectedItem).GetContainerAccessControl() == ContainerAccessControl.Private)
            {
                MessageBox.Show("This container is private. You must download the blob using Get Blob and open the local copy", "Open Blob");
                return;
            }

            Process.Start(prop.Uri.ToString());
        }

        private void OnGetBlob(object sender, ExecutedRoutedEventArgs e)
        {
            BlobProperties prop = (BlobProperties)details.SelectedItem;
            if (prop == null)
            {
                MessageBox.Show("You must select a blob to retrieve");
                return;
            }

            SaveFileDialog dlg = new SaveFileDialog();
            if (prop.Name.Contains('/'))
            {
                dlg.FileName = prop.Name.Substring(prop.Name.LastIndexOf('/') + 1);
            }
            else
            {
                dlg.FileName = prop.Name;
            }
            dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            bool? res = dlg.ShowDialog();

            if (res != null && res.Value)
            {
                BlobContainer container = (BlobContainer)contents.SelectedItem;

                Model.GetBlob(container, prop, dlg.FileName);
            }
        }

        private void OnRefresh(object sender, ExecutedRoutedEventArgs e)
        {
            Model.PopulateContainers();
        }

        private void OnAbout(object sender, ExecutedRoutedEventArgs e)
        {
            About a = new About();
            a.ShowDialog();
        }

        private void OnDeleteBlob(object sender, ExecutedRoutedEventArgs e)
        {
            BlobProperties prop = (BlobProperties)details.SelectedItem;
            if (prop == null)
            {
                MessageBox.Show("You must select a blob to delete");
                return;
            }

            BlobContainer container = (BlobContainer)contents.SelectedItem;
            Model.DeleteBlobs(details.SelectedItems.Cast<BlobProperties>(), container);
        }

        private void PopulateBlobs()
        {
            if (Connected && contents.SelectedItem != null)
            {
                Model.PopulateBlobs((BlobContainer)contents.SelectedItem);
            }
        }

        private void OnAddBlob(object sender, ExecutedRoutedEventArgs e)
        {
            if (contents.SelectedItem == null)
            {
                MessageBox.Show("You must select a container to add blobs to");
                return;
            }

            AddBlob ab = new AddBlob();

            bool? res = ab.ShowDialog();
            if (res != null && res.Value)
            {
                string selectedBlobName = ab.BlobName;
                string fileName = ab.File;


                Model.AddBlob((BlobContainer)contents.SelectedItem, selectedBlobName, fileName, ab.Metadata);
            }
        }

        private void OnDisconnect(object sender, ExecutedRoutedEventArgs e)
        {
            Connected = false;

            this.Title = standardTitle;

            Model.Disconnect();
        }

        private void OnDeleteContainer(object sender, ExecutedRoutedEventArgs e)
        {
            if (contents.SelectedItem == null)
            {
                MessageBox.Show("You must select a container to delete");
                return;
            }

            if (Model.Blobs.Count > 0)
            {
                MessageBoxResult res = MessageBox.Show("This container contains blobs, are you sure you wish to delete it?", 
                                                       "Delete Container", 
                                                        MessageBoxButton.YesNo);
                if (res == MessageBoxResult.No)
                {
                    return; 
                }
            }

            Model.DeleteContainer((BlobContainer)contents.SelectedItem);
        }

        private void OnAddContainer(object sender, ExecutedRoutedEventArgs e)
        {
            AddContainer ac = new AddContainer();
            bool? result = ac.ShowDialog();

            if (result != null && result.Value)
            {
                Model.AddNewContainer(ac.ContainerName, ac.PublicContainer);
            }
        }

        private void OnConnect(object sender, ExecutedRoutedEventArgs e)
        {
            bool devStorage = bool.Parse((string)e.Parameter);
            Uri uri = null;
            string accountName = null;
            string key = null;

            if (devStorage)
            {
                accountName = "devstoreaccount1";
                key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";

                if (ConfigurationManager.AppSettings["devBlobStorageUri"] != null)
                {
                    uri = new Uri(ConfigurationManager.AppSettings["devBlobStorageUri"]);
                }
                else
                {
                    uri = new Uri("http://127.0.0.1:10000");
                }

            }
            else
            {
                uri = new Uri("http://blob.core.windows.net");
                if (ConfigurationManager.AppSettings["cloudAccount"] != null)
                {
                    accountName = ConfigurationManager.AppSettings["cloudAccount"];
                }

                if (ConfigurationManager.AppSettings["cloudKey"] != null)
                {
                    key = ConfigurationManager.AppSettings["cloudKey"];
                }
            }

            StorageAccountInfo info = new StorageAccountInfo(uri, null, accountName, key, true);

            if (!info.IsCompleteSetting())
            {
                Connect conn = new Connect(info);
                bool? ok = conn.ShowDialog();

                if (ok != null && ok.Value)
                {
                    info = conn.StorageInfo;
                }
                else
                {
                    return;
                }

            }

            try
            {
                Model.Connect(info);
                string uriText = null;
                if (devStorage)
                {
                    uriText = info.BaseUri.ToString();
                }
                else
                {
                    uriText = string.Format("http://{0}.{1}", info.AccountName, info.BaseUri.DnsSafeHost);
                }

                this.Title = string.Format("{0} - {1}", standardTitle, uriText);

                Connected = true;
            }
            catch (Exception)
            {
                MessageBox.Show("Connect failed: credential mismatch");
            }
        }
    }
}
